#!/usr/bin/env python
# -*- encoding: utf-8 -*-
'''
@File        :Project_CAN_TC.py
@Desc        :同星CAN
@Time        :2022/06/17 16:42:11
@Author        :yangliang
'''
from ast import If, Return
import binascii
import datetime
from email.headerregistry import Address
from email.message import Message
from gettext import npgettext
from pickle import TRUE
import string
import struct
import time
from pytest_assume.plugin import assume
import sys
import os
import logging
import allure
curPath = os.path.abspath(os.path.dirname(__file__))
rootPath = os.path.abspath(os.path.dirname(curPath) + os.path.sep + ".")
sys.path.append(rootPath)
sys.path.append(os.path.join(curPath,'commDriver'))
sys.path.append(os.path.join(curPath,'data'))
logging.info(sys.path)

from functools import reduce
from commDriver.TS_CAN import TSCAN, TSCAN_UDS,HIL_Obj
from commDriver.TS_CAN import TSMasterAPI
import Set_up
from commDriver.modbus_plc import *
from commDriver.power import power
import numpy as np
from commDriver.zlg.zlgcan import *
from commDriver.zlg.zuds_test_main import *

import crcmod.predefined
from aes_cmac import AESCMAC 


'''计算CRC值'''
def CalCRC8(data):
    polynomial = 0x1D
    initValue = 0x00
    xorValue = 0x00
    for i in range(len(data)):
        initValue = initValue ^ data[i]
        for j in range(8):
            if(initValue & 0x80):
                initValue = ((initValue << 1) & 0xff)
                initValue = initValue ^ polynomial
            else:
                initValue = ((initValue << 1) & 0xff)
    initValue = initValue ^ xorValue
    return initValue


# 回调函数,监控收到某帧报文后作何种操作
def On_CANFD_EVENT_Pre(OBJ, ACAN):

    # global Enable_0x2B6_RollingCounter

    global Enable_0x121_Checksum_Error
    global Enable_0x121_RollingCounter_Error

    
    if ((ACAN.contents.FIdentifier == 0x12D) and (ACAN.contents.FIdxChn == 0)):
        if Enable_0x12D_RollingCounter_Error != True:
            a = ACAN.contents.FData[6] >> 4  # FData 字节数组int类型
            a += 1
            if a > 15:
                a = 0
            ACAN.contents.FData[6] = ((a << 4) & 0xF0) | (ACAN.contents.FData[6] & 0xF)
        else:
            ACAN.contents.FData[6] = 0 | (ACAN.contents.FData[6] & 0xF)
        # Checksum
        if Enable_0x12D_Checksum_Error != True:
            ACAN.contents.FData[7] = sum(ACAN.contents.FData[0:7]) ^ 0xFF
        else:
            ACAN.contents.FData[7] = 0

    if ACAN.contents.FIdxChn == 0 and ACAN.contents.FIdentifier == 0x0C0:
        # readstr = binascii.b2a_hex(bytearray(ACAN2.contents.FData[:8]),' ')
        # logging.info(f'收到0x0C0信号,数据是{readstr}')
        response_map = {
            0x01: [0x02, *Set_up.VIN[7:14]],                    # 第2段: 字节8-14 (7字节)
            0x02: [0x03, *Set_up.VIN[14:]] + [0x00] * 4,        # 第3段: 剩余字节
            0x03: [0x01, *Set_up.VIN[0:7]],                     # 第1段: 字节1-7 (7字节)
            0x00: [0x01, *Set_up.VIN[0:7]]                      # 默认返回第1段
        }
        if ACAN.contents.FData[0] in response_map:
            ACAN.contents.FData[0:8] = response_map[ACAN.contents.FData[0]]



def crc16_table(bytestr):
    '''
    crc16查表法
    :param bytestr: bytes字符串
    :return: int16类型
    '''
    crc = 0xFFFF
    data = bytearray(bytestr)
    len1 = len(bytestr)
    # logging.error('len:[{0}]'.format(len1))
    for i in range(2,len1):
        cp = (crc >> 8) ^ data[i]
        crc = ((crc << 8) & 0xFFFF) ^ mCRC16_Tables[cp]
    return crc

# CRC16 余式0x1021表
mCRC16_Tables=[ 0x0000,0x1021,0x2042,0x3063,0x4084,0x50A5,0x60C6,0x70E7,
                0x8108,0x9129,0xA14A,0xB16B,0xC18C,0xD1AD,0xE1CE,0xF1EF,
                0x1231,0x0210,0x3273,0x2252,0x52B5,0x4294,0x72F7,0x62D6,
                0x9339,0x8318,0xB37B,0xA35A,0xD3BD,0xC39C,0xF3FF,0xE3DE,
                0x2462,0x3443,0x0420,0x1401,0x64E6,0x74C7,0x44A4,0x5485,
                0xA56A,0xB54B,0x8528,0x9509,0xE5EE,0xF5CF,0xC5AC,0xD58D,
                0x3653,0x2672,0x1611,0x0630,0x76D7,0x66F6,0x5695,0x46B4,
                0xB75B,0xA77A,0x9719,0x8738,0xF7DF,0xE7FE,0xD79D,0xC7BC,
                0x48C4,0x58E5,0x6886,0x78A7,0x0840,0x1861,0x2802,0x3823,
                0xC9CC,0xD9ED,0xE98E,0xF9AF,0x8948,0x9969,0xA90A,0xB92B,
                0x5AF5,0x4AD4,0x7AB7,0x6A96,0x1A71,0x0A50,0x3A33,0x2A12,
                0xDBFD,0xCBDC,0xFBBF,0xEB9E,0x9B79,0x8B58,0xBB3B,0xAB1A,
                0x6CA6,0x7C87,0x4CE4,0x5CC5,0x2C22,0x3C03,0x0C60,0x1C41,
                0xEDAE,0xFD8F,0xCDEC,0xDDCD,0xAD2A,0xBD0B,0x8D68,0x9D49,
                0x7E97,0x6EB6,0x5ED5,0x4EF4,0x3E13,0x2E32,0x1E51,0x0E70,
                0xFF9F,0xEFBE,0xDFDD,0xCFFC,0xBF1B,0xAF3A,0x9F59,0x8F78,
                0x9188,0x81A9,0xB1CA,0xA1EB,0xD10C,0xC12D,0xF14E,0xE16F,
                0x1080,0x00A1,0x30C2,0x20E3,0x5004,0x4025,0x7046,0x6067,
                0x83B9,0x9398,0xA3FB,0xB3DA,0xC33D,0xD31C,0xE37F,0xF35E,
                0x02B1,0x1290,0x22F3,0x32D2,0x4235,0x5214,0x6277,0x7256,
                0xB5EA,0xA5CB,0x95A8,0x8589,0xF56E,0xE54F,0xD52C,0xC50D,
                0x34E2,0x24C3,0x14A0,0x0481,0x7466,0x6447,0x5424,0x4405,
                0xA7DB,0xB7FA,0x8799,0x97B8,0xE75F,0xF77E,0xC71D,0xD73C,
                0x26D3,0x36F2,0x0691,0x16B0,0x6657,0x7676,0x4615,0x5634,
                0xD94C,0xC96D,0xF90E,0xE92F,0x99C8,0x89E9,0xB98A,0xA9AB,
                0x5844,0x4865,0x7806,0x6827,0x18C0,0x08E1,0x3882,0x28A3,
                0xCB7D,0xDB5C,0xEB3F,0xFB1E,0x8BF9,0x9BD8,0xABBB,0xBB9A,
                0x4A75,0x5A54,0x6A37,0x7A16,0x0AF1,0x1AD0,0x2AB3,0x3A92,
                0xFD2E,0xED0F,0xDD6C,0xCD4D,0xBDAA,0xAD8B,0x9DE8,0x8DC9,
                0x7C26,0x6C07,0x5C64,0x4C45,0x3CA2,0x2C83,0x1CE0,0x0CC1,
                0xEF1F,0xFF3E,0xCF5D,0xDF7C,0xAF9B,0xBFBA,0x8FD9,0x9FF8,
                0x6E17,0x7E36,0x4E55,0x5E74,0x2E93,0x3EB2,0x0ED1,0x1EF0
            ]


OnCANPreFDevent = TSMasterAPI.OnTx_RxFUNC_CANFD(On_CANFD_EVENT_Pre)


new_rolling_data = []
run_faule_count = 0
corrRespStr = ""

Enable_0x311_Error = False

'''监控PCAN数据'''
def check_PCAN(OBJ, ACAN2):
    global new_rolling_data
    global run_faule_count
    global corrRespStr
    global Enable_0x311_Error
    global random_data
    global send_data


    if ACAN2.contents.FIdxChn == 0 and ACAN2.contents.FIdentifier == Set_up.resID:
        if ACAN2.contents.FData[1:3] == [0x51,0x01] or ACAN2.contents.FData[1:3] == [0x50,0x02]:
            real_CAN_TC.Reset_Setup_Pcanlist(True)
        if ACAN2.contents.FData[1:3] == [0x50,0x01]:
            Set_up.startCheck = True
            BRS = ACAN2.contents.FFDProperties & 0x02 >> 1
            EDL = ACAN2.contents.FFDProperties & 0x01
            logging.info(f'收到5001,brs={BRS}, EDL={EDL}, 开始比对PCAN数据')

    if ACAN2.contents.FIdxChn == 0 and ACAN2.contents.FIdentifier == 0x312:
        readstr = binascii.b2a_hex(bytearray(ACAN2.contents.FData[:8]),' ')
        # logging.info(f'收到0x312信号,数据是{readstr}')

    if ACAN2.contents.FIdxChn == 0 and ACAN2.contents.FIdentifier == 0x0F0:
        readstr = binascii.b2a_hex(bytearray(ACAN2.contents.FData[:8]),' ')
        # logging.info(f'收到0x0F0信号,数据是{readstr}')

    # # 防拆认证
    # if ACAN2.contents.FIdxChn == 0 and ACAN2.contents.FIdentifier == 0x311:
    #     if Enable_0x311_Error != True:
    #         readstr = binascii.b2a_hex(bytearray(ACAN2.contents.FData[:8]),' ')
    #         logging.info(f'收到0x311信号,数据是{readstr},开始防拆认证')
    #         random_data = ACAN2.contents.FData[:8]
    #         keyA = [0xd5,0x4c,0x3f,0x8a,0x9b,0x1e,0x20,0x7d,0x6a,0x48,0x72,0xc3,0x9f,0x0e,0x5b,0x1a]
    #         VIN = Set_up.VIN
    #         # 计算KeyB
    #         keyB = calculate_keyB(VIN, keyA)
    #         # 计算C1
    #         send_data = calculate_C1(random_data, keyB)
    
    # if ACAN2.contents.FIdxChn == 0 and ACAN2.contents.FIdentifier == 0x210:
    #     if Enable_0x311_Error != True:
    #         readstr = binascii.b2a_hex(bytearray(send_data),' ')
    #         logging.info(f'0x210信号发送数据是{readstr}')
    #         ACAN2.contents.FData[:8] = send_data

            
OnCANPreFDevent_checkPCAN = TSMasterAPI.OnTx_RxFUNC_CANFD(check_PCAN)




'''CRC-8校验计算'''
# Crc8_8H2F 多项式0x12F
Crc8_8H2Ftable = [  0x00, 0x2F, 0x5E, 0x71, 0xBC, 0x93, 0xE2, 0xCD, 0x57, 0x78, 0x09, 0x26, 0xEB, 0xC4, 0xB5, 0x9A,
                    0xAE, 0x81, 0xF0, 0xDF, 0x12, 0x3D, 0x4C, 0x63, 0xF9, 0xD6, 0xA7, 0x88, 0x45, 0x6A, 0x1B, 0x34,
                    0x73, 0x5C, 0x2D, 0x02, 0xCF, 0xE0, 0x91, 0xBE, 0x24, 0x0B, 0x7A, 0x55, 0x98, 0xB7, 0xC6, 0xE9,
                    0xDD, 0xF2, 0x83, 0xAC, 0x61, 0x4E, 0x3F, 0x10, 0x8A, 0xA5, 0xD4, 0xFB, 0x36, 0x19, 0x68, 0x47,
                    0xE6, 0xC9, 0xB8, 0x97, 0x5A, 0x75, 0x04, 0x2B, 0xB1, 0x9E, 0xEF, 0xC0, 0x0D, 0x22, 0x53, 0x7C,
                    0x48, 0x67, 0x16, 0x39, 0xF4, 0xDB, 0xAA, 0x85, 0x1F, 0x30, 0x41, 0x6E, 0xA3, 0x8C, 0xFD, 0xD2,
                    0x95, 0xBA, 0xCB, 0xE4, 0x29, 0x06, 0x77, 0x58, 0xC2, 0xED, 0x9C, 0xB3, 0x7E, 0x51, 0x20, 0x0F,
                    0x3B, 0x14, 0x65, 0x4A, 0x87, 0xA8, 0xD9, 0xF6, 0x6C, 0x43, 0x32, 0x1D, 0xD0, 0xFF, 0x8E, 0xA1,
                    0xE3, 0xCC, 0xBD, 0x92, 0x5F, 0x70, 0x01, 0x2E, 0xB4, 0x9B, 0xEA, 0xC5, 0x08, 0x27, 0x56, 0x79,
                    0x4D, 0x62, 0x13, 0x3C, 0xF1, 0xDE, 0xAF, 0x80, 0x1A, 0x35, 0x44, 0x6B, 0xA6, 0x89, 0xF8, 0xD7,
                    0x90, 0xBF, 0xCE, 0xE1, 0x2C, 0x03, 0x72, 0x5D, 0xC7, 0xE8, 0x99, 0xB6, 0x7B, 0x54, 0x25, 0x0A,
                    0x3E, 0x11, 0x60, 0x4F, 0x82, 0xAD, 0xDC, 0xF3, 0x69, 0x46, 0x37, 0x18, 0xD5, 0xFA, 0x8B, 0xA4,
                    0x05, 0x2A, 0x5B, 0x74, 0xB9, 0x96, 0xE7, 0xC8, 0x52, 0x7D, 0x0C, 0x23, 0xEE, 0xC1, 0xB0, 0x9F,
                    0xAB, 0x84, 0xF5, 0xDA, 0x17, 0x38, 0x49, 0x66, 0xFC, 0xD3, 0xA2, 0x8D, 0x40, 0x6F, 0x1E, 0x31,
                    0x76, 0x59, 0x28, 0x07, 0xCA, 0xE5, 0x94, 0xBB, 0x21, 0x0E, 0x7F, 0x50, 0x9D, 0xB2, 0xC3, 0xEC,
                    0xD8, 0xF7, 0x86, 0xA9, 0x64, 0x4B, 0x3A, 0x15, 0x8F, 0xA0, 0xD1, 0xFE, 0x33, 0x1C, 0x6D, 0x42,
                ]

def Crc8_8H2F(pData,len):
    crc8 = 0xFF
    for i in range(len-1):
        crc8 ^= pData[i]
        crc8 = Crc8_8H2Ftable[crc8]
    #     logging.info("Crc8 = {0}".format(crc8))
    # crc8 ^= pData[0]
    # crc8 = Crc8_8H2Ftable[crc8]
    # logging.info("Crc8 = {0}".format(crc8))
    crc8 ^= 0xFF
    return crc8

# modbus = Modbus('COM8')

def calculate_keyB(VIN, keyA):
    '''
    计算KeyB = AES-128-CMAC(KeyA, VIN)
    参数:
        keyA (bytes): 16字节的KeyA密钥
        VIN (bytes): VIN码数据
            
    返回:
        bytes: 16字节的KeyB
    '''
    keyB = AESCMAC.calculate(VIN, keyA)
    # keyBStr = binascii.b2a_hex(bytearray(keyB),' ')
    # logging.info(f'KeyB = [{keyBStr}]')
    return keyB

def calculate_C1(r1, keyB):
    '''
    计算C1 = AES-128-CMAC(KeyB, r1)
        
    参数:
        keyB (bytes): 16字节的KeyB密钥
        r1 (bytes): 随机数r1
            
    返回:
        bytes: 16字节的C1
    '''
    C1 = AESCMAC.calculate(r1, keyB)
    # C1Str = binascii.b2a_hex(bytearray(C1),' ')
    # logging.info(f'C1 = [{C1Str}]')
    return C1[:8]

class real_CAN_TC(TSCAN):
    # 0x311
    def set_0x311_error(self,enable=False):
        global Enable_0x311_Error
        if enable:
            Enable_0x311_Error = True
        else:
            Enable_0x311_Error = False
    
    # def set_0x3D9_checksum_error(self,enable=False):
    #     global Enable_0x3D9_Checksum_Error
    #     if enable:
    #         logging.info("Set 0x3D9 Checksum mistake")
    #         Enable_0x3D9_Checksum_Error = True
    #     else:
    #         logging.info("Set 0x3D9 Checksum correct")
    #         Enable_0x3D9_Checksum_Error = False 

    
    def calcKey(self,seed):
        TOPBIT = 0x8000
        POLYMON_1 = 0x8408   
        POLYNOM_2 = 0x8025
        BITMASK  = 0x0080
        INITIAL_REMINDER = 0xFFFE
        MSG_LEN = 2 #seed length in bytes
        bSeed = [0,0]
        remainder = INITIAL_REMINDER
        bSeed[0] = seed[0]  #MSB
        bSeed[1] = seed[1] #LSB
        key = [0,0]

        for n in range(MSG_LEN):
            remainder ^= ((bSeed[n]&0xFF) << 8)
            for i in range(8):
                if (remainder & TOPBIT):
                    if(remainder & BITMASK):
                        remainder = (remainder << 1) ^ POLYMON_1
                    else:
                        remainder = (remainder << 1) ^ POLYNOM_2
                else:
                    remainder = (remainder << 1)
        key[0] = (remainder&0xFFFF)>>8
        key[1] = remainder&0xFF
        logging.info(key)
        return key

    '''BYD密钥计算'''
    def byd_calcKey(self,seed):
        KeyKValue = 0x0094
        key = [0,0,0,0]

        iSeed = seed[0] << 24|seed[1] << 16|seed[2] << 8 |seed[3]
        KeyResult = ((((iSeed >> 1) ^ iSeed) << 3) ^ (iSeed >> 2))
        STccKey = KeyResult ^ KeyKValue
        key[0] = (STccKey>>24) &0xFF
        key[1] = (STccKey>>16) &0xFF
        key[2] = (STccKey>>8) &0xFF
        key[3] = STccKey &0xFF
        return key

    '''设置信号使能状态'''
    def setSignalEanble(self, message, status, waitTime, iStep):
        if status == False:
            with allure.step(f'Step{iStep}: recover fault of the message {hex(message)} losted'): # type: ignore
                self.Enable_rbs_message(Set_up.NetName,Set_up.nodeName, message, False) 
                # logging.info(f'pause 0x{message} signal send') 
        else:
            with allure.step(f'Step{iStep}: recover fault of the message {message} losted'): # type: ignore
                self.Enable_rbs_message(Set_up.NetName,Set_up.nodeName, message, True) 
                # logging.info(f'recover 0x{message} signal send')
        logging.info(f'wait {waitTime} s') 
        time.sleep(waitTime)
        iStep += 1
        return iStep

    '''设置信号值'''
    def setSignalValue(self, message, signal, value, iStep):
        with allure.step(f'Step{iStep}: set the value of the {signal} in {message} to {value}'):
            self.write_db_signal(Set_up.NetName,Set_up.nodeName, message, signal, value)
            time.sleep(0.5)
            iStep += 1
            readValue = self.read_db_signal(Set_up.NetName,Set_up.nodeName, message, signal)
        with allure.step(f'Step{iStep}: Read the value of the {signal} in {message} is {readValue}'):
            iStep += 1
        return iStep

    '''转换字符串为数组'''
    def strToToHexBytes(self,hexString):
        hexString = str(hexString).replace(' ', '').replace('[', '').replace(']', '').replace('\'', '')

        if ((len(hexString) % 2) != 0):
            hexString += " "

        c_16_temp = list(hexString)
        ilen = int(len(c_16_temp) / 2)
        hexBytes = [0] * ilen
        for index in range(ilen):
            hexBytes[index] = int((c_16_temp[2 * index] + c_16_temp[2 * index + 1]),16)
        return hexBytes
   
    def unlock(self, Driver, iStep, level = 1):
        try:
            seed = []
            if level == 1:
                with allure.step(f"Step{iStep}:Request:0x27 0x01 # Response:0x67,0x01 xx xx xx xx"):
                    uds_rep,size = Driver['UDS'].req_and_res_can([0x27,0x01], 2)
                    with assume: assert (uds_rep[0:2] == [0x67,0x01] and size == 18)
                    for i in range(16):
                        if size == 18:
                            seed.append(uds_rep[2+i])
                        else:
                            seed.append(0)
                    key = AESCMAC.calculate(seed)
                    send_key = [0x27,0x02] + key

                iStep = iStep + 1
                with allure.step(f"Step{iStep}:Request:{0} # Response:".format(binascii.b2a_hex(bytearray(send_key), ' '))):
                    uds_rep,size = Driver['UDS'].req_and_res_can(send_key, 18)
                    with assume: assert uds_rep[0:2] == [0x67,0x02]
            else:
                with allure.step(f"Step{iStep}:Request:0x27 0x11 # Response:0x67,0x11 xx xx xx xx"):
                    uds_rep,size = Driver['UDS'].req_and_res_can([0x27,0x11],2)
                    with assume: assert (uds_rep[0:2] == [0x67,0x11] and size == 18)
                    for i in range(16):
                        if size == 6:
                            seed.append(uds_rep[2+i])
                        else:
                            seed.append(0)
                
                    key = AESCMAC.calculate(seed)
                    send_key = [0x27,0x12] + key

                iStep = iStep + 1
                with allure.step(f"Step{iStep}:Request:{0} # Response:".format(binascii.b2a_hex(bytearray(send_key), ' '))):
                    uds_rep,size = Driver['UDS'].req_and_res_can(send_key,6)
                    with assume: assert uds_rep[0:2] == [0x67,0x12]
            
            return iStep + 1
        except Exception as e:
            logging.info(f"{e}".center(50, '-'))

    ''' Reqstr=发送信息 Respstr=接收信息 session=会话模式 iStep=步骤几 unlock=会话模式切换完成是否需要安全认证'''
    def checkReqResp(self, Driver, Reqstr, Respstr, session, iStep, unlock=False, addressing='UDS', isClearFault='noClearFault'):

        # 判断传入变量类型
        if isinstance(Reqstr, str):
            req = self.strToToHexBytes(Reqstr)
        else:
            req = Reqstr
        if isinstance(Respstr, str):
            resp = self.strToToHexBytes(Respstr)
        else:
            resp = Respstr

        # 会话模式
        if session==1 or session==3 or session == 2:
            with allure.step(f'Step{iStep}: Request 0x10 0x01 # Response: 0x50,0x01'):
                uds_resp, size = Driver['UDS'].req_and_res_can([0x10, 0x01], 2)
                with assume:assert uds_resp[0:2] == [0x50, 0x01]
                iStep = iStep + 1
                time.sleep(1)
            with allure.step(f'Step{iStep}: Request 0x22 0xF1 0x86 # Response: 0x62,0xF1,0x86 0x01'):
                uds_resp, size = Driver['UDS'].req_and_res_can([0x22, 0xF1, 0x86], 3)
                with assume: assert uds_resp[0:4] == [0x62,0xF1,0x86, 0x01]
                iStep = iStep + 1
        if session == 3 or session == 2:
            with allure.step(f'Step{iStep}: Request 0x10 0x03 # Response: 0x50,0x03'):
                uds_resp, size = Driver['UDS'].req_and_res_can([0x10, 0x03], 2)
                with assume:assert uds_resp[0:2] == [0x50, 0x03]
                iStep = iStep + 1
                time.sleep(1)
            with allure.step(f'Step{iStep}: Request 0x22 0xF1 0x86 # Response: 0x62,0xF1,0x86 0x03'):
                uds_resp, size = Driver['UDS'].req_and_res_can([0x22, 0xF1, 0x86], 3)
                with assume: assert uds_resp[0:4] == [0x62,0xF1,0x86, 0x03]
                iStep = iStep + 1
        if session==2:
            # iStep = self.unlock(Driver,iStep)
            with allure.step(f'Step{iStep}: Request 0x10 0x02 # Response: 0x50,0x02'):
                uds_resp, size = Driver['UDS'].req_and_res_can([0x10, 0x02], 2)
                with assume:assert uds_resp[0:2] == [0x50, 0x02]
                iStep = iStep + 1
                time.sleep(2)
            with allure.step(f'Step{iStep}: Request 0x22 0xF1 0x86 # Response: 0x62,0xF1,0x86 0x02'):
                uds_resp, size = Driver['UDS'].req_and_res_can([0x22, 0xF1, 0x86], 3)
                with assume: assert uds_resp[0:4] == [0x62,0xF1,0x86, 0x02]
                iStep = iStep + 1
        if unlock:
            if session == 2:
                iStep = self.unlock(Driver,iStep,2)
            else:
                iStep = self.unlock(Driver,iStep, 1)

        '''清除故障''' 
        if isClearFault != 'noClearFault':
            with allure.step(f"Step{iStep}:Request 0x14 0xFF 0xFF 0xFF # Response: 0x54"):
                uds_rep, size = Driver['UDS'].req_and_res_can([0x14, 0xFF, 0xFF, 0xFF], 4)
                iStep +=1 
                reqstr = binascii.b2a_hex(bytearray(uds_rep), ' ')
                with assume: assert uds_rep[0] == 0x54
                iStep +=1 
                time.sleep(2)

        reqstr = binascii.b2a_hex(bytearray(req), ' ')
        respstr = binascii.b2a_hex(bytearray(resp), ' ')
        
        with allure.step(f'Step{iStep}: Request {reqstr} # Response: {respstr}'): 
            uds_resp, size = Driver[addressing].req_and_res_can(req, len(req))
            
            # if len(uds_resp) == 0 and len(resp) != 0:
            #     # 概率出现无响应问题,重新操作一遍
            #     if addressing == 'UDS':
            #         uds_resp, size = Driver['UDS'].req_and_res_can(req, len(req)) 
            #     else:
            #         uds_resp, size = Driver['UDS'].req_and_res_can(req, len(req), True)


            if len(resp) == 0 :
                with assume: assert 0 == size
            elif len(uds_resp) == 0:
                with assume: assert len(resp) == size
                corrRespStr = binascii.b2a_hex(bytearray(resp), ' ')
                actuRespStr = binascii.b2a_hex(bytearray(uds_resp[0:len(uds_resp)]), ' ')   
                logging.error(f'correct response:[{corrRespStr}], actual response:[{actuRespStr}]') 
            else:    
                if uds_resp[0:len(resp)] != resp:
                    corrRespStr = binascii.b2a_hex(bytearray(resp), ' ')
                    actuRespStr = binascii.b2a_hex(bytearray(uds_resp[0:len(uds_resp)]), ' ')   
                    logging.error(f'correct response:[{corrRespStr}], actual response:[{actuRespStr}]')  
                with assume: 
                    assert uds_resp[0:len(resp)] == resp
                    if req == [0x27,0x01] and resp[0] != 0x7F:
                        assert size == 6

        return iStep + 1,uds_resp, size


    
    def checkReqRespNoWait(self, Driver, Reqstr, Respstr, session, iStep=1, unlock=False, addressing='UDS', isClearFault='noClearFault'):
        """
        检测发送与接收是否符合预期函数,会话切换无等待时间.
        Parameters:
            Driver - Driver. 
            Reqstr - 请求信息
            Respstr - 期待返回信息  
            session - 会话模式  
            iStep - 第几步,默认为步骤1. 
            unlock - 是否安全解锁,默认不安全解锁 
            addressing - 寻址方式,默认物理寻址 
            isClearFault - 是否清除故障,默认不清除    

        Returns:
            iStep - 第几步
            uds_resp - 返回信息 
            size - 信息长度
        """

        # 判断传入变量类型
        if isinstance(Reqstr, str):
            req = self.strToToHexBytes(Reqstr)
        else:
            req = Reqstr
        if isinstance(Respstr, str):
            resp = self.strToToHexBytes(Respstr)
        else:
            resp = Respstr

        # 会话模式
        if session==1 or session==3 or session == 2:
            with allure.step(f'Step{iStep}: Request 0x10 0x01 # Response: 0x50,0x01'):
                uds_resp, size = Driver['UDS'].req_and_res_can([0x10, 0x01], 2)
                with assume:assert uds_resp[0:2] == [0x50, 0x01]
                iStep = iStep + 1
            with allure.step(f'Step{iStep}: Request 0x22 0xF1 0x86 # Response: 0x62,0xF1,0x86 0x01'):
                uds_resp, size = Driver['UDS'].req_and_res_can([0x22, 0xF1, 0x86], 3)
                with assume: assert uds_resp[0:4] == [0x62,0xF1,0x86, 0x01]
                iStep = iStep + 1
        if session == 3 or session == 2:
            with allure.step(f'Step{iStep}: Request 0x10 0x03 # Response: 0x50,0x03'):
                uds_resp, size = Driver['UDS'].req_and_res_can([0x10, 0x03], 2)
                with assume:assert uds_resp[0:2] == [0x50, 0x03]
                time.sleep(0.5)
                iStep = iStep + 1
            with allure.step(f'Step{iStep}: Request 0x22 0xF1 0x86 # Response: 0x62,0xF1,0x86 0x03'):
                uds_resp, size = Driver['UDS'].req_and_res_can([0x22, 0xF1, 0x86], 3)
                with assume: assert uds_resp[0:4] == [0x62,0xF1,0x86, 0x03]
                iStep = iStep + 1
        if session==2:
            # iStep = self.unlock(Driver,iStep)
            with allure.step(f'Step{iStep}: Request 0x10 0x02 # Response: 0x50,0x02'):
                uds_resp, size = Driver['UDS'].req_and_res_can([0x10, 0x02], 2)
                with assume:assert uds_resp[0:2] == [0x50, 0x02]
                time.sleep(1)
                iStep = iStep + 1
            with allure.step(f'Step{iStep}: Request 0x22 0xF1 0x86 # Response: 0x62,0xF1,0x86 0x02'):
                uds_resp, size = Driver['UDS'].req_and_res_can([0x22, 0xF1, 0x86], 3)
                with assume: assert uds_resp[0:4] == [0x62,0xF1,0x86, 0x02]
                iStep = iStep + 1
        if unlock:
            if session == 2:
                iStep = self.unlock(Driver,iStep,2)
            else:
                iStep = self.unlock(Driver,iStep, 1)

        '''清除故障''' 
        if isClearFault != 'noClearFault':
            with allure.step(f"Step{iStep}:Request 0x14 0xFF 0xFF 0xFF # Response: 0x54"):
                uds_rep, size = Driver['UDS'].req_and_res_can([0x14, 0xFF, 0xFF, 0xFF], 4)
                iStep +=1 
                reqstr = binascii.b2a_hex(bytearray(uds_rep), ' ')
                with assume: assert uds_rep[0] == 0x54
                iStep +=1 
                time.sleep(2)
                
        reqstr = binascii.b2a_hex(bytearray(req), ' ')
        respstr = binascii.b2a_hex(bytearray(resp), ' ')
        
        with allure.step(f'Step{iStep}: Request {reqstr} # Response: {respstr}'):
            uds_resp, size = Driver[addressing].req_and_res_can(req, len(req))

            if len(resp) == 0 :
                with assume: assert 0 == size
            elif len(uds_resp) == 0:
                with assume: assert len(resp) == size
                corrRespStr = binascii.b2a_hex(bytearray(resp), ' ')
                actuRespStr = binascii.b2a_hex(bytearray(uds_resp[0:len(uds_resp)]), ' ')   
                logging.error(f'correct response:[{corrRespStr}], actual response:[{actuRespStr}]') 
            else:    
                if uds_resp[0:len(resp)] != resp:
                    corrRespStr = binascii.b2a_hex(bytearray(resp), ' ')
                    actuRespStr = binascii.b2a_hex(bytearray(uds_resp[0:len(uds_resp)]), ' ')   
                    logging.error(f'correct response:[{corrRespStr}], actual response:[{actuRespStr}]')  
                with assume: 
                    assert uds_resp[0:len(resp)] == resp
                    if req == [0x27,0x01] and resp[0] != 0x7F:
                        assert size == 6

        return iStep + 1,uds_resp, size
    
    def Send_And_Receive(self, Driver, Reqstr, iStep):
        # 判断传入变量类型
        if isinstance(Reqstr, str):
            req = self.strToToHexBytes(Reqstr)
        else:
            req = Reqstr
      
        if len(req) < 8:
            req += [0] * (8 - len(req)) 
        reqstr = binascii.b2a_hex(bytearray(req), ' ')

        with allure.step(f'Step{iStep}: Request {reqstr} # Response: '):
            ret = Driver['UDS'].send_frame(Set_up.reqID, req, Set_up.can_Type, True)
            send_start = time.perf_counter()
            # logging.info(f"the send data are <<<{reqstr}>>>")

            raw_frames  = Driver['UDS'].recv_iso_tp_data(Set_up.resID, Set_up.can_Type)
            recv_end = time.perf_counter()
            
            uds_resp = []
            for frame in raw_frames:
                # respstr = binascii.b2a_hex(bytearray(frame['raw_data']), ' ')
                # logging.info(f"[{frame['timestamp']}] {frame['frame_type']}: {respstr}")
                uds_resp.extend(frame['raw_data'])  # 合并所有原始数据

            respstr = binascii.b2a_hex(bytearray(uds_resp), ' ')
            logging.info(f"the read data are <<<{respstr}>>>")
            elapsed = (recv_end - send_start) * 1000

            return iStep + 1, uds_resp, elapsed

    '''设置电源电压'''
    def SetVolt(self, Driver, VoltNumber, waitTime, iStep):
        with allure.step(f"Step{iStep}:Set voltage = {VoltNumber}V"):
            Driver['power'].set_voltage(VoltNumber)
            # logging.info(f'Set volt to {VoltNumber}V')
        iStep += 1
        time.sleep(waitTime)
        logging.info(f'wait {waitTime} s')
        return iStep


    def V_ConnectCanH_GND(self, Eanble,iStep):
        modbus = Modbus(Set_up.modbusCOM)
        if Eanble:
            self.Reset_Setup_Pcanlist(True)
            with allure.step(f"Step{iStep}:VCANH connects to VCANL"):
                modbus.write_single_coil(DO_Address_single.Y0.value,0x0001)
                time.sleep(6)
        else:
            with allure.step(f"Step{iStep}:Remove fault of VCANH connnects to VCANL"):
                modbus.write_single_coil(DO_Address_single.Y0.value,0x0000)
            time.sleep(1)
        iStep += 1
        del modbus

        return iStep

    def set_signal_Year(self,value):
        self.write_db_signal(Set_up.NetName,Set_up.nodeName,'Media_0x2B6','Date_Information_Year_2B6_S',value) 
        self.read_db_signal(Set_up.NetName,Set_up.nodeName,'Media_0x2B6','Date_Information_Year_2B6_S')  

    '''故障注入函数'''
    def setVariantCodeDet(self, Driver, dtcName, PASS=0,iStep=1):
        with allure.step(f'Step{iStep}:故障注入与消除'):
            logging.info("打开外部注入配置信息")
            Driver['CAN2'].sendAndread_CMD_Shell(0x114,'profile 0',10)
            # 打开外部注入配置信息

            
            Driver['CAN2'].sendAndread_CMD_Shell(0x114,f'cmd set gExInj_{dtcName}_Enable 0 C 1 1',10)
            # 设置故障状态 0=PASS -1=FAIL
            if dtcName == 'MasterRdTemp':
                if PASS == 0:
                    PASS = 60.0
                else:
                    PASS = 117.6
                Driver['CAN2'].sendAndread_CMD_Shell(0x114,f'cmd set gExInj_{dtcName}_val 0 f 1 {PASS}',10)
                time.sleep(10)
            else:
                Driver['CAN2'].sendAndread_CMD_Shell(0x114,f'cmd set gExInj_{dtcName}_val 0 s 1 {PASS}',10)

            logging.info(f'{dtcName} 状态为: [{PASS}]') 
            time.sleep(0.5) 
            iStep += 1
            Driver['CAN2'].sendAndread_CMD_Shell(0x114,'cfg_mode 0',10)

        return iStep

    '''断电重启ECU'''
    def restartOBU_Power(self,Driver, iStep=1):
        """
        断电重启产品.
        Parameters:
        Driver - Driver.    
        iStep - 第几步,默认为步骤1.       

        Returns:
            第几步
        """
        with allure.step(f'Step{iStep}:ECU下电再次上电'):
            # Driver['CAN'].pause_signal_loop(0x90)
            # time.sleep(0.5)
            Driver['power'].turn_off_power()
            logging.info('Power off the ECU')
            time.sleep(0.5)
            logging.info('Wait 0.5s')
            Driver['power'].turn_on_power()
            # Driver['CAN'].resume_signal_loop(0x90)
            time.sleep(3)
            logging.info('Wait 3s, Restart the device OK')
            iStep += 1
        return iStep
    
    '''请求1101重启ECU'''
    def restartOBU_11(self,Driver, iStep=1, waitTime=3):
        """
        使用1101重启产品.
        Parameters:
        Driver - Driver.    
        iStep - 第几步,默认为步骤1.       

        Returns:
            第几步
        """
        with allure.step(f'Step{iStep}:request 1101 restart ECU'):
            iStep, uds_resp, size = self.checkReqResp(Driver, "11 01", "51 01", 0, iStep, False)
            logging.info(f"wait {waitTime} s")
            time.sleep(waitTime)
            iStep += 1
        return iStep

    '''读取Pcan数据'''
    def read_PCAN_Frame(self):
        respData = []
        respLen = 0
        uds_resp, uds_resp_size = self.read_frame()
        for i in range(uds_resp_size):
            if uds_resp['AIdentifier'][i] == Set_up.resID:
                if(uds_resp['ADatas'][0][0] & 0xF0)<< 8 == 1: 
                    respLen =  (uds_resp['ADatas'][0][0] & 0x0F) + uds_resp['ADatas'][0][1] +2
                else:
                    respLen =  uds_resp['ADatas'][0][0] + 1
                respData =  uds_resp['ADatas'][i][0:8]
                break
        for i in range(1,uds_resp_size):
            respData += uds_resp['ADatas'][i][1:8]
        if uds_resp_size > 0:
            respData = respData[0:(respLen)]
            actuRespStr = binascii.b2a_hex(bytearray(respData), ' ')    
            logging.error('P_CAN responseID:[0x{0:X}], Data:[{1}]'.format(Set_up.resID, actuRespStr)) 

        return respData, respLen

    '''判断除了三个初始故障是否有新的故障产生'''
    def delInitialFailure(self, iStep,data):
        current_DTC = []
        for real_reponse in np.array(data[3:]).reshape(-1,4):
                    current_DTC.append(real_reponse[0:4].tolist())

        with allure.step(f'Step{iStep}: Determine whether there is any other DTC'):
            for m in Set_up.InitialFailure:
                if m in current_DTC:
                    current_DTC.remove(m)
            with assume: assert len(current_DTC) == 0


            actuRespStr = []
            if len(current_DTC) != 0:
                for m in current_DTC:
                    actuRespStr.append(binascii.b2a_hex(bytearray(m), ' '))

                logging.error('Should not exist DTC iS:[{0}]'.format(actuRespStr)) 
            iStep += 1
        
        return iStep

    '''采用故障注入方式清除初始故障'''
    def clearInitialFilure(self,Driver,iStep):
        with allure.step(f'Step{iStep}: Clear DTC [ACD178 ACD278 ACE655]'):
            logging.info("采用故障注入,清除故障[ACD178 ACD278 ACE655]".center(30, '*'))
            Driver['CAN2'].sendAndread_CMD_Shell(0x114,'profile 0',20)

            Driver['CAN2'].sendAndread_CMD_Shell(0x114,'cmd set gExInj_AlignmentNeverDone_Enable 0 C 1 1',20)
            Driver['CAN2'].sendAndread_CMD_Shell(0x114,'cmd set gExInj_AlignmentNeverDone_val 0 s 1 0',20)

            Driver['CAN2'].sendAndread_CMD_Shell(0x114,'cmd set gExInj_AlignmentNotDone_Enable 0 C 1 1',20)
            Driver['CAN2'].sendAndread_CMD_Shell(0x114,'cmd set gExInj_AlignmentNotDone_val 0 s 1 0',20)

            Driver['CAN2'].sendAndread_CMD_Shell(0x114,'cmd set gExInj_VariantCodeDet_Enable 0 C 1 1',20)
            Driver['CAN2'].sendAndread_CMD_Shell(0x114,'cmd set gExInj_VariantCodeDet_val 0 s 1 0',20)
            time.sleep(1)
            iStep += 1
        return iStep

    def Reset_Setup_Pcanlist(self, Status=True):
        Set_up.startCheck = False
        for i in Set_up.PCAN_list:
            Set_up.PCAN_list[i][1] = True
        for i in Set_up.PCAN_list_BOCH: 
            Set_up.PCAN_list_BOCH[i][1] = True 

    def wakeup_ctrl(self,enable,timecycle=1):
        if enable:
            self.wake_ref = self.send_CANFrame_Cycle(0x188,[0x88,0x88,0x00,0x00,0x00,0x00,0x00,0x88],timecycle)#, Set_up.CAN_Type)
        else:
            self.stop_FrANFrame_Cycle(self.wake_ref)
        pass
    
    def send_sync_frame(self):
        timeNumber = time.time()
        syncTime = int(timeNumber)
        logging.info('send s = [{0}]'.format(syncTime))
        send_SYNC_Frame = [0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x09]
        packed_bytes = struct.pack('<l', syncTime)
        send_SYNC_Frame[4] = packed_bytes[3]
        send_SYNC_Frame[5] = packed_bytes[2]
        send_SYNC_Frame[6] = packed_bytes[1]
        send_SYNC_Frame[7] = packed_bytes[0]
        send_SYNC_Frame[2] = 0 | (send_SYNC_Frame[2] & 0xF0)
        crcData = list(send_SYNC_Frame[0:9])
        crcData.pop(1)
        crc16 = Crc8_8H2F(crcData,8)
        send_SYNC_Frame[1] = crc16
        send_SYNC_Frame.pop()
        self.TransmitData(0x2F6,send_SYNC_Frame)

        return timeNumber


    def send_fup_frame(self, timeNumber):
        fupTime = int((timeNumber % 1) * 1000000000)
        logging.info('send ns = [{0}]'.format(fupTime))
        send_FUP_Frame = [0x28,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x09]
        packed_bytes = struct.pack('<l', fupTime)
        send_FUP_Frame[4] = packed_bytes[3]
        send_FUP_Frame[5] = packed_bytes[2]
        send_FUP_Frame[6] = packed_bytes[1]
        send_FUP_Frame[7] = packed_bytes[0]
        send_FUP_Frame[2] = 0 | (send_FUP_Frame[2] & 0xF0)
        crcData = list(send_FUP_Frame[0:9])
        crcData.pop(1)
        crc16 = Crc8_8H2F(crcData,8)
        send_FUP_Frame[1] = crc16

        send_FUP_Frame.pop()
        self.TransmitData(0x2F6,send_FUP_Frame)


    '''读取Pcan数据'''
    def read_PCAN_Time_Frame(self, enable = False):
        respData = []
        self.ClearBuffer()
        i = 0
        time.sleep(0.1)
        uds_resp, uds_resp_size = self.read_frame()
        for i in range(uds_resp_size):
            if uds_resp['AIdentifier'][i] == 0x66F:
                respData =  uds_resp['ADatas'][i][0:16]
                readTime = uds_resp['ATimeUs'][i]
                if enable == False:
                    i = 20
                    break
                else:
                    if respData[4] == 0x0C:
                        i = 20
                        break

        for i in range(0,uds_resp_size):
            respData += uds_resp['ADatas'][i][0:16]
        if uds_resp_size > 0:
            respData = respData[0:16]
            actuRespStr = binascii.b2a_hex(bytearray(respData), ' ')    
            logging.error('P_CAN responseID:[0x{0:X}], Data:[{1}]'.format(0x66F, actuRespStr)) 

        return respData, time.time() * 1000
        
    
    def SendAndRead_Eol(self, Driver, send_ID, send_Data, receive_ID, timesleep = 0.08, CanType='CAN'):
        sendstr = binascii.b2a_hex(bytearray(send_Data),' ')
        logging.info('send_ID =    [{0:X}], send_Data =    [{1}]'.format(send_ID, sendstr))
        Driver['CAN2'].ClearBuffer()  # 清缓存
        Driver['CAN2'].TransmitData(send_ID,send_Data,CanType)
        time.sleep(timesleep)
        ALl_data,buffer_size = Driver['CAN2'].read_frame()
        response_data = ALl_data[ALl_data['AIdentifier']== receive_ID]
        receive_data = []
        try:
            len = response_data.iloc[0]['ADLC']
            if len < 9:
                lenth = 8 
            if len == 9:
                lenth = 12
            if len == 10:
                lenth = 16
            if len == 11:
                lenth = 20
            if len == 12:
                lenth = 24
            if len == 13:
                lenth = 32
            if len == 14:
                lenth = 48
            if len == 15:
                lenth = 64
            last_data = response_data.iloc[0]['ADatas'] 
            
            receive_data = last_data[0:lenth]
        except:
            receive_data = []
            # logging.info('[0x{0:X}] no response'.format(receive_ID))

        readstr = binascii.b2a_hex(bytearray(receive_data),' ')
        logging.info('receive_ID = [{0:X}], receive_Data = [{1}]'.format(receive_ID,readstr))
        
        return receive_data
    
    '''擦除DID函数'''
    def EraseDID(self, Driver,iStep, area = 0):
        with allure.step(f'Step{iStep}:擦除DID'):
            logging.info("打开外部注入配置信息")
            Driver['CAN2'].sendAndread_CMD_Shell(0x114,'profile 0',10)
            # 打开外部注入配置信息
            if area == 0:
                areaStr = 'DID'
            else:
                areaStr = 'Backup DID'
            respStr =  Driver['CAN2'].sendAndread_CMD_Shell(0x114,f'Test Erase {areaStr}',500)
            respStr = respStr.rstrip()
            time.sleep(0.5) 
            iStep += 1

        Driver['CAN2'].sendAndread_CMD_Shell(0x114,'cfg_mode 0',10)

        return iStep, respStr

    
    def SetEnduranceMode(self, Driver, iStep, Enable=False):
        if Enable:
            # 极限续航模式
            with allure.step(f"Step{iStep}: set Vehicle to Endurance Mode"):
                self.setSignalValue('VCU_0x48B', 'Ext_mode_cfg_self_study', 2, iStep)
                self.setSignalValue('VCU_0x343', 'Ext_mode_open_sts', 1, iStep)
            logging.info('开启极限续航模式')
            Driver['CAN'].checkReqResp(Driver, [0x14,0xFF,0xFF,0xFF], [0x54], 0, iStep)
        else:  
            # 普通驾驶模式
            with allure.step(f"Step{iStep}: set Vehicle to General Driving mode"):
                self.setSignalValue('VCU_0x48B', 'Ext_mode_cfg_self_study', 0, iStep)
                self.setSignalValue('VCU_0x343', 'Ext_mode_open_sts', 0, iStep)
                # 退出极限续航3s后生成故障
                time.sleep(3)
            logging.info('退出极限续航模式')
        return iStep

    '''PCAN 重启指令'''
    def PcanRestart(self, Driver,iStep):
        with allure.step(f'Step{iStep}:PCAN发送指令重启设备'):
            logging.info("打开外部注入配置信息")
            Driver['CAN2'].sendAndread_CMD_Shell(0x114,'profile 0',10)
            # 打开外部注入配置信息
            respStr =  Driver['CAN2'].sendAndread_CMD_Shell(0x114,f'cmd get 0x3000100 0 I 32',500)
            respStr = respStr.rstrip()
            time.sleep(0.5) 
            iStep += 1

        return iStep
    

    def GetMessageCycle(self, Driver, iStep = 1, MessageID = 0x3FF, waitTime = 2):
        MessageCount = []
        logging.info(f'清除缓存,并等待{waitTime}s')
        Driver['CAN'].ClearBuffer()  # 清缓存  
        time.sleep(waitTime)
        ALl_data,buffer_size = Driver['CAN'].read_frame()
        cycle_list = []
        if buffer_size != 0:
            with allure.step(f"Step{iStep}:读取ID[{MessageID:X}]cycle # "):
                response_data = ALl_data[ALl_data['AIdentifier'] == MessageID]
                count =  len(response_data)
                if count > 3:
                    # 计算相邻报文的时间差
                    for i in range(count - 1):
                        if self.configure_baudrate['CAN_Type'] == 'CAN':
                            timeStr = 'ATimeUs'
                        else:
                            timeStr = 'FTimeUs'
                        time_diff = (response_data.iloc[i + 1][timeStr] - response_data.iloc[i][timeStr]) * 1000
                        cycle_list.append(time_diff)
                    logging.info(f'获取到{len(cycle_list)}个周期数据: {cycle_list}毫秒')
                else:
                    logging.warning(f'仅收到{count}帧ID=0x{MessageID:X}的报文,无法计算周期')
            iStep += 1
        return iStep, cycle_list

    def GetMessageCount(self, iStep=1, MessageID=0x312, waitTime=2):
        """读取指定ID的CAN报文数量
        Args:
            Driver: 驱动对象
            iStep: 当前步骤编号
            MessageID: 要读取的CAN ID
            waitTime: 等待时间(秒)
        Returns:
            tuple: (更新后的步骤编号, 报文数量)
        """
        time.sleep(waitTime)
        ALl_data, buffer_size = self.read_frame()
        message_count = 0
        data = []
        
        if buffer_size > 0:
            with allure.step(f"Step{iStep}:读取ID[{MessageID:03X}]数量 # "):
                response_data = ALl_data[ALl_data['AIdentifier'] == MessageID] 
                message_count = len(response_data)
                if message_count > 0:
                    data = response_data.iloc[0]['ADatas'][0:8]
                    dataStr = binascii.b2a_hex(bytearray(data),' ')
                    logging.info(f'获取到ID[{MessageID:03X}]报文数量: {message_count}, 报文内容：{dataStr}')
                else:
                    logging.warning(f'未收到ID[{MessageID:03X}]的报文')
                for i in range(message_count):
                    if data != response_data.iloc[i]['ADatas'][0:8]:
                        data = [0xFF]
                        logging.warning(f'ID[{MessageID:03X}]报文内容不一致')
            iStep += 1

        return iStep, message_count, data  


    def SetVehicleSpeed(self, Driver, iStep = 1, speed = 2, Valid = 0x80):
        # ... existing code ...
        speedvalue = speed / 0.05625
        # 将车速转换为两个字节
        # 使用 struct 模块将浮点数转换为两个字节
        packed = struct.pack('>H', int(speedvalue))
        high_byte = packed[0]
        low_byte = packed[1]
        speed_data = [high_byte, low_byte]
        with allure.step(f"Step{iStep}:Set Vehicle Speed to {speed} # "):
            logging.info(f'设置车速为:{speed}')
            # Driver['CAN'].update_signal_data(0x106, index=[0,1], value=speed_data)
            Driver['CAN'].update_signal_data(0x106, signal_name='VehSpd', value=speed)
        iStep += 1
        return iStep
    
    def SetVehicleSpeedStatus(self, Driver, iStep = 1, Enable = False):
        if Enable == False:
            with allure.step(f"Step{iStep}:pause Vehicle Speed message send# "):
                Driver['CAN'].pause_signal_loop(0x106)
                logging.info('暂停发送车速报文')
        else:
            with allure.step(f"Step{iStep}:resume Vehicle Speed message send# "):
                Driver['CAN'].resume_signal_loop(0x106)
                logging.info('恢复发送车速报文')
        iStep += 1
        return iStep
    
    def SetDLostCommunicationStatus(self, Driver, iStep = 1, Enable = False):
        if Enable == False:
            with allure.step(f"Step{iStep}:pause FZCU_BodyCAN_0x90 message send # "):
                Driver['CAN'].pause_signal_loop(0x90)
                logging.info('暂停发送FZCU_BodyCAN_0x90报文')
        else:
            with allure.step(f"Step{iStep}:resume FZCU_BodyCAN_0x90 message send # "):
                Driver['CAN'].resume_signal_loop(0x90)
                logging.info('恢复发送FZCU_BodyCAN_0x90报文')
        iStep += 1
        return iStep
    
    
    def SetJiDianQiStatus(self, Driver, iStep = 1, iPort=6, Status = 0):
        with allure.step(f"Step{iStep}: set switch port{iPort} to {Status} # "):
            Driver['jidianqi'].SetPort(iPort, Status)
            logging.info(f'设置继电器{iPort}为{"OFF" if Status == 0 else "ON"}')  # 0=断开 1=闭合
        iStep += 1
        return iStep
    
    def recv_frame_by_id(self, Driver, ID, timeout = 3):
        """接收指定ID的CAN帧,带超时处理
        Args:
            Driver: 驱动对象
            ID: 要接收的CAN帧ID
            timeout: 超时时间(秒),默认3秒
        Returns:
            respData: 接收到的数据
        Raises:
            TimeoutError: 超时未收到指定ID的报文
        """
        Driver['CAN'].fush_q_buff()  # 清缓存
        start_time = time.time()
        
        while True:
            # 检查是否超时
            if time.time() - start_time > timeout:
                return None  # 超时返回None
            ALl_data, buffer_size = Driver['CAN'].read_frame()
            if buffer_size > 0:
                response_data = ALl_data[ALl_data['AIdentifier'] == ID]
                if not response_data.empty:
                    respData = response_data.iloc[0]['ADatas']
                    actuRespStr = binascii.b2a_hex(bytearray(respData), ' ') 
                    logging.info('responseID:[0x{0:X}], Data:[{1}]'.format(ID, actuRespStr))
                    return respData
            time.sleep(0.1)

    def check_nm_state(self, Driver):
        """
        检查当前的NM状态,通过解析0x560报文获取状态信息
        Returns:
            dict: 包含以下状态信息的字典:
                - RMR_state: bool (第二字节第1位)
                - active_wake: bool (第二字节第5位) 
                - RMS_state: bool (第三字节第1位)
        """
        # 读取0x563报文
        nm_data = self.recv_frame_by_id(Driver, 0x563)
        if nm_data is not None: 
            readstr = binascii.b2a_hex(bytearray(nm_data),' ')
            logging.info(f"NM DATA : {readstr}")
        else:
            logging.info("read 3s,No NM data received")
        
        if not nm_data or len(nm_data) < 3:
            return {
                'RMR_state': None,
                'active_wake': None,
                'RMS_state': None,
                'error': 'Invalid NM frame data'
            }
        
        # 解析状态位
        byte1 = nm_data[1] if len(nm_data) > 1 else 0
        byte2 = nm_data[2] if len(nm_data) > 2 else 0
        
        return {
            'RMR_state': byte1 & 0x01,     # 第二字节第1位
            'active_wake': byte1 & 0x10,  # 第二字节第5位
            'RMS_state': byte2 & 0x01,    # 第三字节第1位
            'raw_data': nm_data
        }

    def readValue_byID(self, ReadID, timeout=5.0, isClearBuffer = True):
        """
        根据ID读取CAN数据，增加超时处理
        Parameters:
            ReadID - 要读取的CAN ID
            timeout - 超时时间(秒)，默认5秒
        Returns:
            respData - 读取到的数据，超时返回None
        """
        if isClearBuffer:
            self.ClearBuffer()
        time.sleep(0.1)
        start_time = time.time() 
        while True:
            ALl_data, buffer_size = self.read_frame()
            if buffer_size > 0:
                response_data = ALl_data[ALl_data['AIdentifier'] == ReadID]
                if not response_data.empty:
                    return response_data.iloc[0]['ADatas']
            
            if time.time() - start_time > timeout:
                logging.warning(f"Timeout while waiting for CAN ID {hex(ReadID)}")
                return None
            time.sleep(0.1)

    '''读取防拆认证状态'''
    def getOBUDismountSt(self, iStep):
        '''
        读取OBU防拆认证状态
        Returns:
            OBUDismountSt - OBU防拆认证状态
        '''
        # OBU防拆认证状态定义
        OBU_DISMOUNT_STATUS = {
            0: "Success - 防拆认证成功",
            1: "Fail - 防拆认证失败", 
            2: "Reserved - 预留",
            3: "Default - 默认值(信号初始值)"
        }
        with allure.step(f'Step{iStep}: 从EtcmInfoCanFr01里获取OBU防拆状态[OBUStatusOBUDismountSt]# '):
            OBUDismountSt = self.read_db_signal(Set_up.NetName, 'ETCM', 'EtcmInfoCanFr01', 'OBUStatusOBUDismountSt')
            OBUDismountSt = int(OBUDismountSt)
            # readValue = self.readValue_byID(0x3FF)
            # OBUDismountSt = (readValue[0] >> 2) & 0x03
            status_desc = OBU_DISMOUNT_STATUS.get(OBUDismountSt, "Unknown status")
            logging.info(f'OBUDismountSt = {OBUDismountSt} ({status_desc})')
            iStep += 1
        return iStep, OBUDismountSt

    '''读取ETC激活状态'''
    def getETCActiveSt(self, iStep):
        '''
        读取ETC激活状态
        Returns:
            ETCActiveSt - ETC激活状态
        '''
        # ETC激活状态
        ETC_ACTIVE_STATUS = {
            0: "Actvn - 激活",
            1: "Deactivn - 未激活(信号初始值)",
            2: "Reserved - 预留",
            3: "Default - 默认值"
        }
        with allure.step(f'Step{iStep}: 从EtcmInfoCanFr01里获取ETC激活状态[OBUStatusETCActiveSt] # '):
            readValue = self.readValue_byID(0x3FF)
            ETCActiveSt = (readValue[0] >> 6) & 0x03
            status_desc = ETC_ACTIVE_STATUS.get(ETCActiveSt, "Unknown status")
            logging.info(f'读取ETC激活状态:ETCActiveSt = {ETCActiveSt} ({status_desc})')
            iStep += 1
        return iStep, ETCActiveSt

    '''读取自检状态'''
    def getOBUSelfCheckSt(self, iStep):
        # obu自检状态
        OBU_SELFCHECK_STATUS = {
            0: "Normal - 正常(信号初始值)",
            1: "Abnormal - 异常",
            2: "Reserved - 预留",
            3: "Default - 默认值"
        }
        with allure.step(f'Step{iStep}: 从EtcmInfoCanFr01里获取OBU自检状态[OBUStatusOBUSelfCheckSt] # '):
            readValue = self.readValue_byID(0x3FF)
            OBUSelfCheckSt = readValue[0] & 0x03
            status_desc = OBU_SELFCHECK_STATUS.get(OBUSelfCheckSt, "Unknown status")
            logging.info(f'OBUSelfCheckSt = {OBUSelfCheckSt} ({status_desc})')
            iStep += 1
        return iStep, OBUSelfCheckSt

    '''读取OBU开关状态'''
    def getOBUSwitchSt(self, iStep):
        # OBU开关状态
        OBU_SWITCH_STATUS = {
            0: "Default - 默认值",
            1: "Off - 关(激活前信号初始值)",
            2: "On - 开(激活后信号初始值)",
            3: "Reserved - 预留"
        }
        with allure.step(f'Step{iStep}: 从EtcmInfoCanFr01里获取OBU开关状态[OBUStatusOBUSwitchSt] # '):
            OBUSwitchSt = self.read_db_signal(Set_up.NetName,'ETCM', 'EtcmInfoCanFr01', 'OBUStatusOBUSwitchSt')
            # readValue = self.readValue_byID(0x3FF, False)
            # OBUSwitchSt = (readValue[1] >> 6) & 0x03
            status_desc = OBU_SWITCH_STATUS.get(OBUSwitchSt, "Unknown status")
            logging.info(f'OBUSwitchSt = {OBUSwitchSt} ({status_desc})')
            iStep += 1
        return iStep, OBUSwitchSt

    '''读取OBU蓝牙状态'''
    def getOBUBLESt(self, iStep):
        # OBU开关状态
        OBU_BLE_STATUS = {
            0: "Default - 默认值(信号初始值)",
            1: "Off - 关",
            2: "On - 开",
            3: "Reserved - 预留"
        }
        with allure.step(f'Step{iStep}: 从EtcmInfoCanFr01里获取OBU蓝牙状态[OBUStatusOBUBLESt] # '):
            OBUBLESt = self.read_db_signal(Set_up.NetName,'ETCM', 'EtcmInfoCanFr01', 'OBUStatusOBUBLESt')
            # readValue = self.readValue_byID(0x3FF)
            # OBUBLESt = (readValue[0] >> 4) & 0x03
            status_desc = OBU_SWITCH_STATUS.get(OBUBLESt, "Unknown status")
            logging.info(f'OBUBLESt = {OBUBLESt} ({status_desc})')
            iStep += 1
        return iStep, OBUBLESt

    '''读取出入口状态'''
    def getOBUGateProperty(self, iStep):
        # 出入口状态定义
        OBU_GATE_PROPERTY = {
            1: "Entry - 入口",
            2: "Exit - 出口",
            4: "Reserved - 预留",
            0: "Default - 默认值"
        }
        with allure.step(f'Step{iStep}: 从EtcmInfoCanFr04里获取出入口状态[OBUTradeInfoOBUGateProperty] # '):
            # readValue = self.readValue_byID(0x312)
            # OBUGateProperty = readValue[0]
            OBUGateProperty = self.read_db_signal(Set_up.NetName,'ETCM', 'EtcmInfoCanFr04', 'OBUTradeInfoOBUGateProperty')
            status_desc = OBU_GATE_PROPERTY.get(OBUGateProperty, "Unknown status")
            logging.info(f'OBUGateProperty = {OBUGateProperty} ({status_desc})')
            iStep += 1
        return iStep, OBUGateProperty

    '''读取ETC通行状态'''
    def getOBUSystemSt(self, iStep):
        # ETC通行状态定义
        OBU_SYSTEM_STATUS = {
            0: "通行正常",
            1: "通行异常",
            2: "联系运行商",
            3: "无卡"
        }
        with allure.step(f'Step{iStep}: 从EtcmInfoCanFr04里获取ETC通行状态[OBUTradeInfoOBUSystemSt] # '):
            OBUTradeInfoOBUSystemSt = self.read_db_signal(Set_up.NetName,'ETCM', 'EtcmInfoCanFr04', 'OBUTradeInfoOBUSystemSt')
            status_desc = OBU_SYSTEM_STATUS.get(OBUTradeInfoOBUSystemSt, "Unknown status")
            logging.info(f'OBUSystemSt = {OBUTradeInfoOBUSystemSt} ({status_desc})')
            iStep += 1
        return iStep, OBUTradeInfoOBUSystemSt

    '''读取交易金额'''
    def getOBUTradeMoney(self, iStep):
        with allure.step(f'Step{iStep}: 从EtcmInfoCanFr04里获取交易金额[OBUTradeInfoOBUTradeMoney] # '):
            OBUTradeMoney = self.read_db_signal(Set_up.NetName,'ETCM', 'EtcmInfoCanFr04', 'OBUTradeInfoOBUTradeMoney')
            logging.info(f'OBUTradeMoney = {OBUTradeMoney:.2f}元')
            iStep += 1
        return iStep, OBUTradeMoney

    def setOBUSwitchSt(self, iStep: int, Enable: bool = False) -> int:
        """
        设置OBU开关状态
        Args:
            iStep: 当前测试步骤编号
            Enable: 是否开启ETC功能, True=开启, False=关闭
        Returns:
            int: 更新后的步骤编号
        """
        # 清除缓存
        self.ClearBuffer()
        ETC_Switch_Data = [0x00] * 8  # 初始化数据
        ETC_Switch_Data[0] = 0x02 if Enable else 0x04
        action = "开启" if Enable else "关闭"
        value = 1 if Enable else 2

        with allure.step(f'Step{iStep}: 设置DhuInfoCanFr72信号的ETCSwitchReq值为{value} # '):
            logging.info(f'设置ETCSwitch为:{action}')
            self.TransmitData(0x189, ETC_Switch_Data, Set_up.CAN_Type)
            iStep += 1
            time.sleep(0.5)
        with allure.step(f'Step{iStep}: 读取OBU开关状态 # '):
            iStep, obuSwitch = self.getOBUSwitchSt(iStep)
            logging.info(f'读取OBU开关状态为:{obuSwitch}')

        return iStep, obuSwitch

    '''读取组包结果'''
    def getETCActiveRespFromOBU(self, iStep):
        # ETC通行状态定义
        ETC_ActiveResp_FromOBU = {
            0: "Default - 默认值",
            1: "Ok - 组包成功",
            2: "Nok - 组包失败",
            3: "Reserved - 预留"
        }
        with allure.step(f'Step{iStep}: 从EtcmInfoCanFr04里获取ETC组包结果[ETCActiveRespFromOBU] # '):
            ETCActiveRespFromOBU = self.read_db_signal('DC1E','ETCM', 'EtcmInfoCanFr04', 'ETCActiveRespFromOBU')
            status_desc = ETC_ActiveResp_FromOBU.get(ETCActiveRespFromOBU, "Unknown status")
            logging.info(f'ETCActiveRespFromOBU = {ETCActiveRespFromOBU} ({status_desc})')
            iStep += 1
        with allure.step(f'Step{iStep}: 读取ETCActiveRespFromOBU发送帧数 # '):
            iStep, messageCount, data = self.GetMessageCount(iStep, 0x312, 1)
            iStep += 1
        return iStep, ETCActiveRespFromOBU, messageCount

    '''告知OBU处于出入口之间,不允许关闭ETC功能'''
    def getETCActiveCmdFromOBU(self, iStep):
        '''
        告知OBU处于出入口之间,不允许关闭ETC功能
        '''
        # ETC通行状态定义
        ETC_ActiveCMD_FromOBU = {
            0: "出口",
            1: "处于出入口之间",
            8: "无效值",
        }
        with allure.step(f'Step{iStep}: 从EtcmInfoCanFr04里获取[ETCActiveCmdFromOBU] # '):
            ETCActiveCmdFromOBU = self.read_db_signal('DC1E','ETCM', 'EtcmInfoCanFr04', 'ETCActiveCmdFromOBU')
            ETCActiveCmdFromOBU_UB = self.read_db_signal('DC1E','ETCM', 'EtcmInfoCanFr04', 'ETCActiveCmdFromOBU_UB')
            if ETCActiveCmdFromOBU_UB != 0x01:
                ETCActiveCmdFromOBU = 8
            status_desc = ETC_ActiveCMD_FromOBU.get(ETCActiveCmdFromOBU, "Unknown status")
            logging.info(f'ETCActiveCmdFromOBU = {ETCActiveCmdFromOBU} ({status_desc})')
            iStep += 1
        with allure.step(f'Step{iStep}: 读取ETCActiveCmdFromOBU发送帧数 # '):
            iStep, messageCount, data = self.GetMessageCount(iStep, 0x312, 1)
            iStep += 1
        return iStep, ETCActiveCmdFromOBU, messageCount

    def unpack(self, send_Data):
        """优化后的分包函数，确保所有数据包(包括不足5字节的)都会添加包头信息
        Args:
            send_Data: 要发送的数据列表
        Returns:
            list: 包含所有包的列表，每个包都包含数据和包头信息
        """
        data_len = len(send_Data)
        dataID = 0x01  # 数据ID
        
        # 计算总包数
        total_packets = max(1, (data_len + 4) // 5)  # 确保至少有1个包
        packets = []
        
        for seq in range(1, total_packets + 1):
            start = (seq - 1) * 5
            end = min(seq * 5, data_len)  # 防止索引越界
            
            # 构造包头
            packet_header = [
                data_len,  # 数据总长度
                ((total_packets & 0x3F) << 2) | ((dataID & 0x03)),  # 第一个字节
                ((dataID & 0x0C) << 6) | ( seq & 0x3F)  # 第二个字节
            ]
            
            # 获取当前包的数据部分
            packet_data = send_Data[start:end]
            # 如果数据不足5字节，填充0
            if len(packet_data) < 5:
                packet_data += [0x00] * (5 - len(packet_data))
                
            packets.append(packet_data + packet_header)

        # # 打印所有包的信息
        # for idx, packet in enumerate(packets):
        #     packet_str = ' '.join(f'{byte:02X}' for byte in packet)
        #     logging.info(f'Packet {idx + 1}: {packet_str}')
            
        return packets

    def pack(self, packet_data, data_id, receive_packets):
        """处理接收到的数据包并更新接收状态
        Args:
            packet_data: 接收到的数据包
            data_id: 预期的数据ID
            receive_packets: 已接收的数据包字典
        Returns:
            tuple: (是否完成接收, 合并后的数据)
        """
        try:
            # 解析包头信息
            seq_num = packet_data[7] & 0x3F  # 序列号(低6位)
            packet_num = (packet_data[6] >> 2) & 0x3F  # 总包数(高6位)
            recv_data_id = ((packet_data[6] & 0x03)) | ((packet_data[7] >> 6) & 0x03 << 2)  # 数据ID(第一个字节低2位+第二个字节高2位)
            data_len = packet_data[5]
            
            # 检查数据ID是否匹配
            # if recv_data_id != data_id:
            #     return False, None
                
            # 存储有效数据部分
            receive_packets[seq_num] = packet_data[0:5]
            # 打印接收到的数据包信息
            # packet_str =''.join(f'{byte:02X}' for byte in packet_data)
            # logging.info(f'Received Packet: Seq={seq_num}, Total={packet_num}, DataLen={data_len}, Data={packet_str}')
            
            # 检查是否收到所有分包
            if len(receive_packets) == packet_num:
                # 按序列号排序并合并数据
                sorted_packets = [receive_packets[i] for i in sorted(receive_packets.keys())]
                receive_data = []
                for pkt in sorted_packets:
                    receive_data.extend(pkt)
                    receive_data = receive_data[:data_len]  # 确保数据长度正确
                return True, receive_data
                
        except Exception as e:
            logging.warning(f"处理响应数据时出错: {str(e)}")
            
        return False, None

    def ZEEKR_Send_Recv(self, send_Data, time_out=1, CanType='CAN'):
        """发送CAN帧并循环接收响应,支持超时机制
        Args:
            Driver: 驱动对象
            send_ID: 发送帧ID
            send_Data: 发送数据
            receive_ID: 接收帧ID
            time_out: 超时时间(秒),默认1秒
            CanType: CAN类型,默认'CAN'
        Returns:
            receive_data: 接收到的数据，超时返回空列表
        """
        # 清缓存并发送数据
        self.ClearBuffer()
        time.sleep(0.2)

        sendstr = binascii.b2a_hex(bytearray(send_Data), ' ')
        logging.info(f'send_ID = [0x{Set_up.ActiveReqID:03X}], send_Data = [{sendstr}]')

        packets = self.unpack(send_Data)

        # 循环发送所有数据
        for packet in packets:
            self.TransmitData(Set_up.ActiveReqID, packet, CanType)
            time.sleep(0.01)  # 短暂休眠避免CPU占用过高
        
        # 设置超时时间
        start_time = time.time()
        receive_packets = {}  # 存储接收到的分包数据
        total_packets = 0     # 总包数
        data_id = 0x01        # 数据ID

        time.sleep(0.1)  # 短暂休眠避免CPU占用过高
        
        # 循环读取直到超时或收到完整数据
        while (time.time() - start_time) < time_out:
            ALl_data, buffer_size = self.read_frame()
            if buffer_size > 0:
                response_data = ALl_data[ALl_data['AIdentifier'] == Set_up.ActiveResID]
                if not response_data.empty:
                    for i in range(len(response_data)):
                        is_complete, receive_data = self.pack(
                            response_data.iloc[i]['ADatas'], 
                            data_id,
                            receive_packets
                        )
                        if is_complete:
                            readstr = binascii.b2a_hex(bytearray(receive_data), ' ')
                            logging.info(f'收到完整数据: ID=[{Set_up.ActiveResID:X}], Data=[{readstr}]')
                            return receive_data
            time.sleep(0.05)
            
        logging.warning(f'未在{time_out}秒内收到完整响应数据(收到{len(receive_packets)}/{total_packets}包)')
 
        return []

    def Set_ETCActiveRespFromHMI(self, iStep, Status = 'Sussess'):
        '''设置EtcmInfoCanFr04信号的ETCActiveRespFromHMI值'''
        send_data = [0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00]
        if (Status == 'Sussess'):
            value = 1
            send_data[6] = 0x0C
        else:
            value = 2
            send_data[6] = 0x14
        
        with allure.step(f'Step{iStep}: 设置DhuInfoCanFr75信号的ETCActiveRespFromHMI值为{value} # '):
            logging.info(f'设置DhuInfoCanFr75信号的ETCActiveRespFromHMI值为{value}')
            self.TransmitData(0x0E1, send_data, Set_up.CAN_Type)
            iStep += 1
        return iStep

    def send_ETCActiveCmd(self, iStep, value = 0x01):
        '''发送ETCActiveCmdFromHMI'''
        send_ID = 0x0E1
        send_data = [0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00]
        send_data[6] = (value << 6) | 0x20 | (send_data[6] & 0x1F)
        with allure.step(f'Step{iStep}: 发送ETCActiveCmdFromHMI信号 # '):
            logging.info(f'发送ETCActiveCmdFromHMI信号,请求开启防拆认证')
            self.TransmitData(send_ID, send_data, Set_up.CAN_Type)
            iStep += 1
        return iStep

    def send_UXCDismountData(self, iStep, data = None):
        '''发送UXCDismountData 进行防拆认证'''
        dataStr = binascii.b2a_hex(bytearray(data),' ')
        keyA = [0xd5,0x4c,0x3f,0x8a,0x9b,0x1e,0x20,0x7d,0x6a,0x48,0x72,0xc3,0x9f,0x0e,0x5b,0x1a]
        # keyAStr = binascii.b2a_hex(bytearray(keyA),' ')
        # logging.info(f'KeyA = [{keyAStr}]')
        VIN = Set_up.VIN
        # vinStr = binascii.b2a_hex(bytearray(VIN),' ')
        # logging.info(f'VIN = [{vinStr}]')
        # 计算KeyB
        keyB = calculate_keyB(VIN, keyA)
        # 计算C1
        send_data = calculate_C1(data, keyB)
        send_ID = 0x210
        with allure.step(f'Step{iStep}: 发送UXCDismountData信号 # '):
            logging.info(f'发送随机数 = [{dataStr}]的UXCDismountData信号')
            self.TransmitData(send_ID, send_data, Set_up.CAN_Type)
            iStep += 1
        return iStep

    def getOBUSoftwareVersion(self, Driver, iStep):
        '''
        读取OBU版本号 【 ApduData 】 : 【 AA5506020E8C040280】
        '''
        test_str = ''
        with allure.step(f'Step{iStep}: 读取OBU版本号 # '):
            logging.info(f'读取OBU版本号')
            # Driver['M600'].InitRSU(channel_id='01')  # 初始化RSU
            test_str = Driver['M600'].SendBST()  # 发送BST
            # test_str = Driver['M600'].GetSecure()  # 获取Secure
            apdu1 = 'AA 55 06 02 0E 8C 04 02 80'
            SoftwareVersion = Driver['M600'].TransferChannel(channelID='BB', Apdus=apdu1)  # 传输通道
            test_str = Driver['M600'].SetMMI()  # 设置MMI.
            Driver['M600'].EventReport()
            iStep += 1

        return iStep, SoftwareVersion

    def SetOBUTradeInfo(self, Driver, iStep, value):
        ETC_Trade_Info = {
            0: "通行正常",
            1: "通行异常",
            2: "联系运行商",
            3: "无卡"
        }
        status_desc = ETC_Trade_Info.get(value, "Unknown status")
        with allure.step(f'Step{iStep}: 设置OBU交易状态为: {status_desc} # '):
            logging.info(f'设置OBU交易状态为: {status_desc}')
            # Driver['M600'].InitRSU(channel_id='01')  # 初始化RSU
            test_str = Driver['M600'].SendBST()  # 发送BST
            test_str = Driver['M600'].SetMMI(Paramter = str(value).zfill(2))  # 设置MMI
            Driver['M600'].EventReport()
            iStep += 1

        return iStep

    def SetOBUTradeMoney(self, Driver, iStep, iMoney = 0, Entrance_Exit_Status = 3):
        
        ETC_Trade_Info = {
            0: "",
            1: "封闭MTC入口",
            2: "封闭MTC出口",
            3: "封闭ETC入口",
            4: "封闭ETC出口",
            5: "MTC开放式入口",
            6: "MTC开放式出口",
        }
        status_desc = ETC_Trade_Info.get(Entrance_Exit_Status, "Unknown status")
        with allure.step(f'Step{iStep}: 设置OBU交易金额与出入口状态: {iMoney}分 {status_desc} # '):
            logging.info(f'设置OBU交易金额与出入口状态: {iMoney}分 {status_desc}')
            # Driver['M600'].InitRSU(channel_id='01')  # 初始化RSU
            Driver['M600'].etc_transaction(iMoney = iMoney, Entrance_Exit_Status = Entrance_Exit_Status)
            iStep += 1
        return iStep

    def setOBUTampersSt(self, Driver, iStep, value = 0):
        '''设置OBU防拆认证状态'''
        OBU_DISMOUNT_STATUS = {
            0: "已拆卸",
            1: "未拆卸",
        }
        status_desc = OBU_DISMOUNT_STATUS.get(value, "Unknown status")
        with allure.step(f'Step{iStep}: 设置OBU防拆认证状态为: {status_desc} # '):
            logging.info(f'设置OBU防拆认证状态为: {status_desc}')
            status = Driver['M600'].write_obu_tamper(tamper_status = value)
        return iStep, status

    def getOBUTampersSt(self, Driver, iStep):
        '''5.8G 获取OBU防拆认证状态'''
        with allure.step(f'Step{iStep}: 获取OBU防拆认证状态 # '):
            status = Driver['M600'].read_obu_tamper()
        return iStep, status 

# run main
if __name__ == "__main__":
    import os
    root = os.getcwd()
    project_DBC_File_CAN = os.path.join(root, r'data\MRR_20220901.dbc')
    logging.info(project_DBC_File_CAN)
    try:


        rbs = real_CAN_TC(drive_type = 'USE_TS_TC1011_DEVICE',    # USE_TS_TC1014_DEVICE /   USE_TS_VIRTUAL_DEVICE
                    configure_baudrate = {'CAN_Type':'CANFD','ArbRate':500,'DataRate':2000,'TermResistor':True}
                    )
        rbs.load_dbc(project_DBC_File_CAN)
        # rbs.register_event_can_T_R(OnCANevent)
        # rbs.register_event_canfd_T_R(OnCANFDevent)
        rbs.set_0x222_rolling_counter_error(enable=False)
        # rbs.register_event_canfd_T_pre(OnCANPreFDevent)
        
        rbs.connect_application()
        rbs.RBS_Config('Vehicle_CAN',['BCM'])

        logging.info('send frame')
        rbs.TransmitData(0x666,[0x61,2,3,4,5,6,7,8,
                              0x22,2,3,4,5,6,7,8,
                              0x23,2,3,4,5,6,7,8,
                              0x24,2,3,4,5,6,7,8,
                              0x25,2,3,4,5,6,7,8,
                              0x26,2,3,4,5,6,7,8,
                              0x27,2,3,4,5,6,7,8,
                              0x28,2,3,4,5,6,7,8],'CANFD')
        rbs.TransmitData(0x777,[0x71,2,3,4,5,6,7,8,],'CAN')
        rbs.TransmitData(0x777,[0x81,2,3,4,5,6,7,8,],'CAN')
        rbs.TransmitData(0x444,[0x41,2,3,4,5,6,7,8,],'CAN')

        a =rbs.send_CANFrame_Cycle(0x22,[0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88],100)
        
        logging.info('step1_wait_1000_:::::')
        rbs.ClearBuffer()
        rbs.TS_delay(1000)
        rbs.read_frame()

        logging.info('step2__wait_2000_:::::') 
        rbs.ClearBuffer()
        rbs.TS_delay(2000)
        rbs.read_frame()

        logging.info('step2__wait_2000_:::::') 
        rbs.ClearBuffer()
        rbs.TS_delay(2000)
        rbs.read_frame()

        logging.info('step3__wait_3000_:::::') 
        rbs.ClearBuffer()
        rbs.TS_delay(3000)
        rbs.read_frame()

        ttttt = rbs.sendAndread_CMD_Shell(0x114,'test 12',500)
        ttttt = rbs.sendAndread_CMD_Shell(0x114,'test 12',500)
        logging.info(ttttt)
        rbs.stop_FrANFrame_Cycle(a)

        rbs.write_db_signal('SCAN4','GW','ABS1','ABS1_FLWheelSpd',34.0)

        aa = rbs.read_db_signal('SCAN4','GW','ABS1','ABS1_FLWheelSpd')
        logging.info('read rbs signal value is ',aa)

        # diag = TSCAN_UDS()
        # diag.req_and_res_can([0x22,0xf1,0x90],3)
        # diag.UDS_can_delete()

        # obj = HIL_Obj(rbs)
        # obj.setDaemon(True)
        # obj.open()
        # obj.obj_sent()
        # obj.start()
        # obj.last_warninh_data
        # obj.warning_data

        rbs.dis_connect_application()

        logging.info('Over All')
        import sys
        sys.exit(0)

    except Exception as e :
        rbs.dis_connect_application()
        import sys
        import traceback
        logging.info(traceback.print_exc())
        sys.exit(0)
        pass
    
    except KeyboardInterrupt :
        rbs.dis_connect_application()
        import sys
        import traceback
        logging.info(traceback.print_exc())
        sys.exit(0)
        pass
